/*
Bullet Continuous Collision Detection and Physics Library
Copyright (c) 2003-2006 Erwin Coumans  http://continuousphysics.com/Bullet/

This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose, 
including commercial applications, and to alter it and redistribute it freely, 
subject to the following restrictions:

1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#ifndef TRAIN_DEMO_H
#define TRAIN_DEMO_H

class btCollisionShape;

#include "GlutDemoApplication.h"



/*
 * This sample application uses Bullet Physics to model a toy train running around a track.
 * It features signals, a can controller and rail junctions too !
 *
 * The basic idea is to make a rigid body follow a given trajectory. The simplest trajectory is a
 * straight line. To make a closed path, one can use a circle or one can join together straight lines
 * & circular curves. So as the rigid body moves along the path, its "handed off" to the next curve at
 * just the right moment when it comes to the end of the current curve.
 *
 * This idea can be extended to a simple trajectory constraint which contains a list of curves.
 *
 *
 * In the context of this sample, the curves are called Track segments. They can be straight or circular
 * curves. A railway track is simply a list of track segments. The track is only drawn but not modeled
 * as a rigid body.
 *
 * The railroad car is a cuboidal rigid body. These are the only rigid bodies in this sample. All the rest
 * are collision shapes.
 *
 * No other features of Bullet like constraints, are used. All forces are calculated and applied directly on the
 * cars. The front most is car 0 and is the engine.
 *
 */



//Utility function used to draw a vector
void 	drawVector(btVector3& from, btVector3& to);

//Makes printing vectors easy !
#define BT3ARGS(V) V.x(), V.y(), V.z()


//Now we start this demo specific stuff

//Increase this to make a longer train
//NOTE: Dont make it higher than 6, otherwise you will need to place the cars on the track yourself in defineCars() !
const int MAX_CARS = 3;

//This can be made as long as required to create more complex tracks that take you anywhere.
const int MAX_SEGMENTS = 20;

//Track segment types supported, SPIRAL does not work yet, approximate them using circular segments
enum SegmentType{
	STRAIGHT,
	CIRCULAR,
	SPIRAL
};


/*
 * Maximum signals per track segment. Signals just consist of 3 lights.
 */
const int MAX_SEGMENT_SIGNALS = 2;

//Signal values for train control
enum SignalLampType{
	OFF,
	RED,
	YELLOW,
	GREEN
};


/*
 * Speed setpoints achieved by the cab controller upon spotting a signal. The cab controller looks at the
 * signal setting ahead of it and accelerates/decelerates to acheive the proper speed.
 *
 * Unfortunately it does not do so gradually !
 */
const float MAX_GREEN_SPEED = 100.0f;
const float MAX_YELLOW_SPEED = 40.0f;


/**
 * Generic track segment base struct with common properties for all segments
 * It accepts arguments for modeling a straight  segment by default.
 * For curvd segments, look at
 *
 * struct CircularArcSegment: public TrackSegment
 *
 * below.
 *
 * A TrackSegment is a single node in the linked list of segments forming a single railroad track.
 */
struct TrackSegment
{
	//A number to access this segment in the track linked list, used for debugging
	int segmentID;

	//2 types
	SegmentType segmentType;

	//The position and the orientation in space of the 3 axes. endAxis is for the last radial direction where the
	//curve ends
	btVector3 origin, forwardAxis, upAxis, rightAxis, endAxis; //circle start axis is in forwardAxis(angle=0)

	//Lenght of a straight segment
	btScalar length;

	//angle of arc for circular segment
	btScalar angle;

	//Doubly linked list to enable forward and backward motion of cars.
	TrackSegment *prev, *next; 


	/* Junction specific management
	 * A junction's info is stored in the junction's input track segment only
	 * When a junction is activated, its output track segment's  inJunctionActive must be set to true
	 * This output segment is got by junctionLastSegment->next->inJunctionActive = true
	 */
	bool inJunctionActive, outJunctionActive, derailed;
	TrackSegment *inJunctionLastSegment; //the injunction last segment is required for backward flow
	TrackSegment *outJunctionFirstSegment, *outJunctionLastSegment; //out junction info here mainly
	btScalar outLength, inLength;

	//Signals
	int numSignals;
	btScalar signalDist[MAX_SEGMENT_SIGNALS]; //Max. 1 platform in a segment
	SignalLampType signalLamp[MAX_SEGMENT_SIGNALS];


	//Platforms
	btScalar platformStart, platformEnd;
	btRigidBody *platform;


	TrackSegment();	

	TrackSegment(btVector3& originIn, btVector3& forwardAxisIn, btVector3& upAxisIn,
			btScalar lengthIn)
	{
		origin = originIn;
		forwardAxis = forwardAxisIn;
		upAxis = upAxisIn;
		rightAxis = upAxis.cross(forwardAxis);
		length = lengthIn;

		inJunctionActive = false;
		outJunctionActive = false;
		outJunctionFirstSegment = NULL;
		derailed = false;

		numSignals = 0;
		platformStart = 0;
		platformEnd = 0;
		platform = NULL;
	}

	virtual void drawSegment()
	{
		TrackSegment *i;

		//Draw junction
		if(!outJunctionActive)
			glColor3f(1., 0., 0.);
		
		if(outJunctionFirstSegment)
			for(i=outJunctionFirstSegment; i != outJunctionLastSegment->next; i = i->next)
				i->drawSegment();

		//Draw track
		btVector3 to = origin + forwardAxis* length;
		glColor3f(0., 0., 0.);
		glBegin(GL_LINES);
			glVertex3f(BT3ARGS(origin));
			glVertex3f(BT3ARGS(to));
		glEnd();
	}


	//Toggle flags in input AND output segments of the junction
	void toggleJunction(void)
	{
		outJunctionActive = !outJunctionActive;
		outJunctionLastSegment->next->inJunctionActive = !outJunctionLastSegment->next->inJunctionActive;
	}
};


/**
 * Curved segment for getting cars around bends, constant radius 
 */
struct CircularArcSegment: public TrackSegment
{
	btVector3 startRadius, pnt;

	CircularArcSegment();	

	CircularArcSegment(btVector3& startPointIn, btVector3& forwardAxisIn, btVector3& upAxisIn,
				btScalar lengthIn,	btScalar angleIn):
		TrackSegment(startPointIn, forwardAxisIn, upAxisIn, lengthIn)
	{
		origin = startPointIn - forwardAxisIn*length;
		angle = angleIn;
		endAxis = forwardAxis.rotate(upAxis, angle);
	}

	virtual void drawSegment()
	{
		//draw vector for length1 magn forwardAxis from where the curve starts
		startRadius = forwardAxis * length;
		pnt = startRadius + origin;

	
		//Draw junctions : no support in circular tracks yet



		//Draw track
		glBegin(GL_LINES);
			glVertex3f(BT3ARGS(origin));
			glVertex3f(BT3ARGS(pnt));

			
			if(angle < 0.){
				//draw the curve
				const float radIncr = -0.01f;
				for(float theta = 0.; theta > (angle + SIMD_EPSILON); theta+=radIncr)
				{
					glVertex3f(BT3ARGS(pnt));

					pnt = startRadius.rotate(upAxis, theta) + origin;

					glVertex3f(BT3ARGS(pnt));
				}
			}
			else{
				//draw the curve
				const float radIncr = +0.01f;
				for(float theta = 0.; theta < (angle + SIMD_EPSILON); theta+=radIncr)
				{
					glVertex3f(BT3ARGS(pnt));

					pnt = startRadius.rotate(upAxis, theta) + origin;

					glVertex3f(BT3ARGS(pnt));
				}
				
			}

			glVertex3f(BT3ARGS(origin));
			glVertex3f(BT3ARGS(pnt));

		glEnd();

	}



};



struct TrainCar
{
	btRigidBody* m_carChassis;
	btVector3 origin;					//world space position
	TrackSegment *segment[2], *prevSegment[2];
	btScalar prevDist[2];			//last distance along line, separate angle will be reqd for circle junctions
};


//The Train application sample , shows how to setup and use a train
class Train : public GlutDemoApplication
{
	//Bullet world variables
	btAlignedObjectArray<btCollisionShape*> m_collisionShapes;
	class btBroadphaseInterface*	m_overlappingPairCache;
	class btCollisionDispatcher*	m_dispatcher;
	class btConstraintSolver*	m_constraintSolver;
	class btDefaultCollisionConfiguration* m_collisionConfiguration;


	//Camera control
	float		m_cameraHeight, chassisHeight;
	bool		cameraEnabled;
	float		m_minCameraDistance;
	float		m_maxCameraDistance;


	//Track geometry info, there are  tracks to demo junctions
	int numSegsTrack1, numSegsTrack2;


	//Railroad car transform
	btTransform transCar;
	btVector3 projPlane, posWheel, relPosWheel, radiusWheel, dr, dv, force;  //there are wrt Origin
	btVector3 projPosFRPlane, projPosUFPlane, deviation, wheelRightAxis;  //these are positions, after adding segment origin to get position
	btScalar speed, targetSpeed, connectionHeight, k;
	btVector3 carHalfDimensions, platformHalfDimensions;
	btScalar alpha, currentAngle;
	bool reversed;



	//Gap calculation
	btVector3 xtrans, ytrans, ztrans;

	
	
	//Car wheels
	btScalar wheelLeftRightHalfDist, wheelFwdBackHalfDist;

	//Car link lengths behind/after chassis box Z limit
	btScalar linkLength;


	//Signal drawing
	btCollisionShape *signalPoleShape, *signalLampShape;


	//Application user interface
	btCollisionShape *selectedShape;
	TrackSegment *currentSelTrkSeg;
	int currentSelSignal;
	


	//-------------------------------------------
	void 	initPhysics();

	//Railroad car updating stuff
	void	defineCars(void);
	void	updateCars(float dt);
	int		cabControl(btVector3& ztrans);

	//Track Management stuff : could be separated later
	void initializeSegment(TrackSegment* pTS, SegmentType segmentType, int segmentID);
	int createTrack1(void);
	int createTrack2(void);
	int createJunction1(void);
	int createJunction2(void);
	int createRailInfrastructure();
	int deleteTrackSegment(TrackSegment *pTS);
	int insertTrackSegment(TrackSegment *ref, TrackSegment *pTS);

	//TODO: Free track segments in this function, otherwise there will be a memory leak
	int freeTracks();

	//Camera focus
	void	focusCameraOnTrain();
	void	focusCameraOnSignal();
	void	focusCameraOnJunction();

	//Drawing functions called from renderme()
	void	drawSignals(TrackSegment *i, btVector3& worldBoundsMin, btVector3& worldBoundsMax);


public:
	Train();

	virtual ~Train();

	virtual void 	clientMoveAndDisplay();

	virtual void	clientResetScene();

	virtual void 	displayCallback();
	
	///a very basic camera following the vehicle
	virtual void updateCamera();

	virtual void specialKeyboard(int key, int x, int y);

	virtual void specialKeyboardUp(int key, int x, int y);

	void renderme();



	static DemoApplication* Create()
	{
		Train* demo = new Train();
		demo->myinit();
		demo->initPhysics();
		return demo;
	}




};







#endif //TRAIN_DEMO_H


